﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using ThreeByte.ActiveDeck.Data.UserResponse;

namespace ThreeByte.ActiveDeck.Data
{
    public static class UserDataUtil
    {
        private static readonly XNamespace ns = Service.ServiceConfiguration.NAMESPACE;


        #region MultipleChoice
        public static void RecordMultipleChoiceAnswer(string presentation, int section, string username, XElement answer, DateTime timestamp) {
            ActiveDeckDataContext db = DataUtil.GetDataContext();

            MultipleChoice mc = new MultipleChoice();
            mc.PresentationID = presentation;
            mc.Section = section;
            mc.Page = int.Parse(answer.Attribute("Page").Value);
            mc.UserName = username;
            mc.Timestamp = timestamp;
            mc.Answer = int.Parse(answer.Attribute("Value").Value);

            db.MultipleChoices.InsertOnSubmit(mc);
            db.SubmitChanges();
        }

        public static void RecordMultipleChoiceAnswer(string presentation, int section, int page, int value) {
            ActiveDeckDataContext db = DataUtil.GetDataContext();

            MultipleChoice mc = new MultipleChoice();
            mc.PresentationID = presentation;
            mc.Section = section;
            mc.Page = page;
            mc.UserName = "Anonymous";
            mc.Timestamp = DateTime.Now;
            mc.Answer = value;

            db.MultipleChoices.InsertOnSubmit(mc);
            db.SubmitChanges();
        }

        public static bool HasMultipleChoiceData(string presentation, int section, int page) {
            ActiveDeckDataContext db = DataUtil.GetDataContext();

            var anAnswer = (from a in db.MultipleChoices
                            where a.PresentationID == presentation
                            && a.Section == section
                            && a.Page == page
                            select a).FirstOrDefault();
            return (anAnswer != null);
        }

        public static bool HasMultipleChoiceData(string presentation, int page) {
            ActiveDeckDataContext db = DataUtil.GetDataContext();

            var anAnswer = (from a in db.MultipleChoices
                            where a.PresentationID == presentation
                            && a.Page == page
                            select a).FirstOrDefault();
            return (anAnswer != null);
        }

        public static MultipleChoiceUserData GetMultipleChoiceUserData(string presentation, int section, int page) {

            ActiveDeckDataContext db = DataUtil.GetDataContext();

            MultipleChoiceUserData userData = new MultipleChoiceUserData(page);

            var answers = from a in db.MultipleChoices
                          where a.PresentationID == presentation
                          && a.Section == section
                          && a.Page == page
                          group a by a.Answer into g
                          select new {
                              Answer = g.Key,
                              Count = g.Count()
                          };

            foreach(var a in answers) {
                MultipleChoiceAnswerData aData = new MultipleChoiceAnswerData(a.Answer);
                aData.UserResponses = a.Count;
                userData.Answers.Add(aData);
            }

            return userData;
        }

        //
        public static Dictionary<int, AggregateUserData> GetMultipleChoiceGroupedUserData(string presentation, int section, int page) {

            ActiveDeckDataContext db = DataUtil.GetDataContext();

            var answers = from a in db.MultipleChoices
                          join ug in db.UserGroups on new { a.PresentationID, a.UserName } equals new { ug.PresentationID, UserName = ug.User }
                          where a.PresentationID == presentation
                          && a.Section == section
                          && a.Page == page
                          group a by new { a.Answer, ug.GroupID } into g
                          select new
                          {
                              Group = g.Key.GroupID,
                              Answer = g.Key.Answer,
                              Count = g.Count()
                          };

            Dictionary<int, AggregateUserData> groupedUserData = new Dictionary<int, AggregateUserData>();

            foreach(var a in answers) {
                if(!groupedUserData.ContainsKey(a.Group)) {
                    groupedUserData[a.Group] = new MultipleChoiceUserData(page);
                }
                MultipleChoiceAnswerData aData = new MultipleChoiceAnswerData(a.Answer);
                aData.UserResponses = a.Count;
                ((MultipleChoiceUserData)(groupedUserData[a.Group])).Answers.Add(aData);                               
            }
            
            return groupedUserData;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="presentation">Presentation ID</param>
        /// <param name="pages">Dictionary of PageID and Correct Answer</param>
        /// <returns></returns>
        public static Dictionary<int, float> GetMultipleChoiceGroupedResultsUserData(string presentation, Dictionary<int, int> pages) {
            ActiveDeckDataContext db = DataUtil.GetDataContext();

            var answers = from mc in db.MultipleChoices
                          join ug in db.UserGroups on new { mc.PresentationID, mc.UserName } equals new { ug.PresentationID, UserName = ug.User }
                          where mc.PresentationID == presentation
                          && pages.Keys.Contains(mc.Page)
                          group mc by new { ug.GroupID, mc.Page, mc.UserName } into g
                          join q in db.MultipleChoices on g.Max(t => t.Timestamp) equals q.Timestamp
                          where g.Key.Page == q.Page && g.Key.UserName == q.UserName
                          orderby g.Key.UserName, g.Key.Page
                          select new
                          {
                              Group = g.Key.GroupID,
                              TimeStamp = g.Max(t => t.Timestamp),
                              Page = g.Key.Page,
                              UserName = g.Key.UserName,
                              Answer = q.Answer
                          };
             
            //Group ID and Percentage of Questions Answered Correctly
            Dictionary<int, float> aggGroupedUserData = new Dictionary<int, float>();

            foreach(var a in answers) {
                if(!aggGroupedUserData.ContainsKey(a.Group)) {
                    aggGroupedUserData.Add(a.Group, 0);
                }

                aggGroupedUserData[a.Group] += (float)pages.Where(p => p.Key == a.Page && p.Value == a.Answer).Count();
            }

            foreach(int k in new List<int>(aggGroupedUserData.Keys)) {
                aggGroupedUserData[k] = ((float)aggGroupedUserData[k] / (float)answers.Where(p => p.Group == k).Count()) * 100.0f;
            }

            return aggGroupedUserData;

        }

        #endregion Multiple Choice

        #region Short Answer
        public static void RecordShortAnswer(string presentation, int section, string username, XElement answer, DateTime timestamp) {

            ActiveDeckDataContext db = DataUtil.GetDataContext();

            ShortAnswer newAnswer = new ShortAnswer();
            newAnswer.PresentationID = presentation;
            newAnswer.Section = section;
            newAnswer.Page = int.Parse(answer.Attribute("Page").Value);
            newAnswer.UserName = username;
            newAnswer.Timestamp = timestamp;
            newAnswer.Answer = answer.Attribute("Text").Value;

            db.ShortAnswers.InsertOnSubmit(newAnswer);
            db.SubmitChanges();
        }

        public static void RecordShortAnswer(string presentation, int section, int page, string value) {

            ActiveDeckDataContext db = DataUtil.GetDataContext();

            ShortAnswer newAnswer = new ShortAnswer();
            newAnswer.PresentationID = presentation;
            newAnswer.Section = section;
            newAnswer.Page = page;
            newAnswer.UserName = @"Anonymous";
            newAnswer.Timestamp = DateTime.Now;
            newAnswer.Answer = value;

            db.ShortAnswers.InsertOnSubmit(newAnswer);
            db.SubmitChanges();
        }

        public static bool HasShortAnswerData(string presentation, int section, int page) {
            ActiveDeckDataContext db = DataUtil.GetDataContext();

            var anAnswer = (from a in db.ShortAnswers
                            where a.PresentationID == presentation
                            && a.Section == section
                            && a.Page == page
                            select a).FirstOrDefault();
            return (anAnswer != null);
        }

        public static ShortAnswerUserData GetShortAnswerUserData(string presentation, int section, int page) {

            ActiveDeckDataContext db = DataUtil.GetDataContext();

            ShortAnswerUserData userData = new ShortAnswerUserData(page);

            var answers = from a in db.ShortAnswers
                          where a.PresentationID == presentation
                          && a.Section == section
                          && a.Page == page
                          orderby a.Timestamp ascending
                          select a.Answer;

            foreach(string s in answers) {
                userData.Answers.Add(s);
            }
            return userData;
        }

        #endregion Short Answer


        #region Categories
        public static void RecordCategoriesAnswer(string presentation, int section, string username, XElement answer, DateTime timestamp) {
            ActiveDeckDataContext db = DataUtil.GetDataContext();

            CategorySubmission newSubmission = new CategorySubmission();
            newSubmission.PresentationID = presentation;
            newSubmission.Section = section;
            newSubmission.Page = int.Parse(answer.Attribute("Page").Value);
            newSubmission.UserName = username;
            newSubmission.Timestamp = timestamp;

            foreach(XElement cat in answer.Elements(ns + "Category")) {
                foreach(XElement i in cat.Elements(ns + "Item")) {
                    CategoryItem newItem = new CategoryItem();
                    newItem.Item = int.Parse(i.Attribute("ID").Value);
                    newItem.Category = int.Parse(cat.Attribute("ID").Value);
                    newSubmission.CategoryItems.Add(newItem);
                }
            }

            db.CategorySubmissions.InsertOnSubmit(newSubmission);
            db.SubmitChanges();
        }

        public static void RecordCategoriesAnswers(string presentation, int section, int page, Dictionary<int, int> answers) {
            ActiveDeckDataContext db = DataUtil.GetDataContext();

            CategorySubmission newSubmission = new CategorySubmission();
            newSubmission.PresentationID = presentation;
            newSubmission.Section = section;
            newSubmission.Page = page;
            newSubmission.UserName = "Anonymous";
            newSubmission.Timestamp = DateTime.Now;

            foreach(int i in answers.Keys) {
                CategoryItem newItem = new CategoryItem();
                newItem.Item = i;
                newItem.Category = answers[i];
                newSubmission.CategoryItems.Add(newItem);
            }

            db.CategorySubmissions.InsertOnSubmit(newSubmission);
            db.SubmitChanges();
        }

        public static bool HasCategoriesData(string presentation, int section, int page) {
            ActiveDeckDataContext db = DataUtil.GetDataContext();

            var anAnswer = (from a in db.CategorySubmissions
                            where a.PresentationID == presentation
                            && a.Section == section
                            && a.Page == page
                            select a).FirstOrDefault();
            return (anAnswer != null);
        }

        public static CategoriesUserData GetCategoriesUserData(string presentation, int section, int page) {

            ActiveDeckDataContext db = DataUtil.GetDataContext();

            CategoriesUserData userData = new CategoriesUserData(page);

            //TODO: eliminate duplicates?
            var submissions = from s in db.CategorySubmissions
                              where s.PresentationID == presentation
                              && s.Section == section
                              && s.Page == page
                              select s;

            foreach(CategorySubmission sub in submissions) {
                userData.IncrementSubmissions();
                foreach(CategoryItem item in sub.CategoryItems) {
                    userData.AddResponse(item.Item, item.Category);
                }
            }

            return userData;
        }

        #endregion Categories

        //For an entire presentation
        public static void ResetUserData(string presentation) {

            ActiveDeckDataContext db = DataUtil.GetDataContext();

            //Multiple Choice
            {
                var answers = from a in db.MultipleChoices
                              where a.PresentationID == presentation
                              select a;
                db.MultipleChoices.DeleteAllOnSubmit(answers);
            }

            //Short Answer
            {
                var answers = from a in db.ShortAnswers
                              where a.PresentationID == presentation
                              select a;
                db.ShortAnswers.DeleteAllOnSubmit(answers);
            }

            //Categories
            {
                var answers = from a in db.CategorySubmissions
                              where a.PresentationID == presentation
                              select a;
                foreach(CategorySubmission sub in answers) {
                    db.CategoryItems.DeleteAllOnSubmit(sub.CategoryItems);
                }
                db.CategorySubmissions.DeleteAllOnSubmit(answers);
            }

            db.SubmitChanges();
        }

        public static void ResetUserData(string presentation, int section, int page) {

            ActiveDeckDataContext db = DataUtil.GetDataContext();

            //Multiple Choice
            {
                var answers = from a in db.MultipleChoices
                              where a.PresentationID == presentation
                              && a.Section == section
                              && a.Page == page
                              select a;

                db.MultipleChoices.DeleteAllOnSubmit(answers);
            }

            //Short Answer
            {
                var answers = from a in db.ShortAnswers
                              where a.PresentationID == presentation
                              && a.Section == section
                              && a.Page == page
                              select a;

                db.ShortAnswers.DeleteAllOnSubmit(answers);
            }

            //Categories
            {
                var answers = from a in db.CategorySubmissions
                              where a.PresentationID == presentation
                              && a.Section == section
                              && a.Page == page
                              select a;
                foreach(CategorySubmission sub in answers) {
                    db.CategoryItems.DeleteAllOnSubmit(sub.CategoryItems);
                }
                db.CategorySubmissions.DeleteAllOnSubmit(answers);
            }

            db.SubmitChanges();
        }


    }
}
